import React, {useCallback, useRef} from 'react';
import {
    DndContext,
    DragEndEvent,
    DragStartEvent,
    DragCancelEvent,
    DragOverEvent,
    DragOverlay,
    rectIntersection,
    closestCenter,
    pointerWithin,
    getFirstCollision,
    MeasuringStrategy,
    DropAnimation,
    defaultDropAnimationSideEffects,
    CollisionDetection,
} from '@dnd-kit/core';
import {SortableContext, verticalListSortingStrategy} from '@dnd-kit/sortable';
import {arrayMove} from '@dnd-kit/sortable';
import {useQueryClient} from '@tanstack/react-query';
import {QueryKeys} from '@/lib/queryClient';
import {useReorderCollections} from '@/hooks/api/useCollections';
import {useReorderTabs} from '@/hooks/api/useTabs';
import {useActiveSpaceStore} from '@/stores/activeSpace';
import {DraggableCollectionCard} from '@/components/cards/DraggableCollectionCard';
import {CollectionCard} from '@/components/cards/CollectionCard';
import {Button} from '@/components/ui/Button';
import {FolderOpen, Search, Loader} from 'lucide-react';
import type {SpaceCollection, TabItem, CollectionWithTabsDto} from '@/types';
import {ScrollContainer} from '@/components/ui/ScrollContainer';
import {TabCard} from '@/components/cards/TabCard';
import {calculateDragPositionUpdates} from '@/lib/utils';
import {useFilteredSpaceCollections} from '@/hooks/dashboard/useFilteredSpaceCollections';

/**
 * CollectionsGrid 组件的 props 类型定义
 */
interface CollectionsGridProps {
    // 搜索查询字符串（用于内部过滤）
    searchQuery: string;
    // 是否正在保存
    isSaving: boolean;
    // 添加集合处理函数
    onAddCollection: () => void;
}

/**
 * 集合网格组件
 * 职责：
 * - 直接从 Query 获取集合数据（确保拖拽排序时立即响应缓存更新）
 * - 根据搜索条件过滤集合
 * - 处理集合和标签的拖拽排序
 * - 提供空状态和加载状态展示
 * 
 * 注意：集合数据派生通过 useFilteredSpaceCollections 统一完成，
 * 该 Hook 直接消费 TanStack Query 缓存，保证拖拽排序的乐观更新在本组件内即时落地。
 */
export const CollectionsGrid: React.FC<CollectionsGridProps> = ({
    searchQuery,
    isSaving,
    onAddCollection,
}) => {
    // 获取当前活跃空间 ID
    const activeSpaceId = useActiveSpaceStore((s) => s.activeSpaceId);

    const {filteredCollections, totalTabsCount, collectionsCount, isLoading} =
        useFilteredSpaceCollections(activeSpaceId, searchQuery);

    // Query Client（用于组件中同步更新缓存）
    const queryClient = useQueryClient();

    // 使用 TanStack Query mutations
    const reorderCollectionsMutation = useReorderCollections();
    const reorderTabsMutation = useReorderTabs();

    // 拖拽状态
    const [activeId, setActiveId] = React.useState<string | null>(null);
    const [previewCollections, setPreviewCollections] = React.useState<Record<number, SpaceCollection> | null>(null);
    const previewCollectionsRef = useRef<Record<number, SpaceCollection> | null>(null);
    const previewTabLocationsRef = useRef<Map<number, number> | null>(null);
    const lastOverId = useRef<string | null>(null);
    const recentlyMovedToNewContainer = useRef(false);
    const isDragEnabled = !searchQuery.trim();

    const collectionPrefix = 'collection-';
    const tabPrefix = 'tab-';

    const isCollectionId = (id: string) => id.startsWith(collectionPrefix);
    const isTabId = (id: string) => id.startsWith(tabPrefix);

    const parseCollectionId = (id: string): number | null => {
        if (!isCollectionId(id)) return null;
        const value = Number(id.slice(collectionPrefix.length));
        return Number.isNaN(value) ? null : value;
    };

    const parseTabId = (id: string): number | null => {
        if (!isTabId(id)) return null;
        const value = Number(id.slice(tabPrefix.length));
        return Number.isNaN(value) ? null : value;
    };

    const resetPreviewState = useCallback(() => {
        previewCollectionsRef.current = null;
        previewTabLocationsRef.current = null;
        setPreviewCollections(null);
        recentlyMovedToNewContainer.current = false;
    }, []);

    // 确保"预览状态（preview）"已构建：
    // 该函数在拖拽 Tab 过程中，为了做到"所见即所得"的预览效果。
    const ensurePreviewState = useCallback((): Record<number, SpaceCollection> => {
        // 如果之前已经构建过快照（用户可能持续拖拽），直接复用，避免重复拷贝开销
        if (previewCollectionsRef.current) {
            return previewCollectionsRef.current;
        }

        // `snapshot`：以 collectionId 作为 key 的集合快照，用于 UI 预览渲染
        const snapshot: Record<number, SpaceCollection> = {};
        // `locationMap`：记录每个 tab 当前所在的 collectionId，便于快速定位
        const locationMap = new Map<number, number>();

        // 遍历当前屏幕展示的集合，逐个进行**深拷贝**（collection 与 tabs 都要拷，避免后续预览操作影响原数据）
        for (const collection of filteredCollections) {
            snapshot[collection.collectionId] = {
                ...collection,                              // 浅拷贝集合对象的顶层字段
                collection: {...collection.collection},    // 深拷贝嵌套的 collection 元信息
                tabs: collection.tabs.map((tab) => ({...tab})), // 深拷贝 tabs，确保后续重排只影响预览
            };

            // 同时为每个 tab 建立 `tabId -> collectionId` 定位，便于 onDragOver/resolveTabCollectionId 快速查询
            for (const tab of collection.tabs) {
                locationMap.set(tab.id, collection.collectionId);
            }
        }

        previewCollectionsRef.current = snapshot;
        previewTabLocationsRef.current = locationMap;
        // 触发一次状态更新，让 UI 立即切换到"预览数据"渲染（不改真实数据，只改预览）
        setPreviewCollections(snapshot);

        // 返回快照给调用方（通常是 onDragStart/onDragOver）继续基于它做预览重排
        return snapshot;
    }, [filteredCollections]);

    /**
     * 解析标签页所属的集合 ID
     * 优先从预览状态获取（拖拽过程中可能已移动），否则直接使用 tab.collectionId
     */
    const resolveTabCollectionId = useCallback(
        (tabId: number): number | null => {
            const previewLocations = previewTabLocationsRef.current;
            if (previewLocations?.has(tabId)) {
                return previewLocations.get(tabId) ?? null;
            }

            // 直接从 filteredCollections 中查找 tab 并返回其 collectionId
            for (const collection of filteredCollections) {
                const tab = collection.tabs.find(t => t.id === tabId);
                if (tab) {
                    return tab.collectionId;
                }
            }

            return null;
        },
        [filteredCollections]
    );

    /**
     * 自定义碰撞检测（collisionDetection）：
     *
     * 目标：
     * 1) 当拖拽的是“集合卡片（collection-*）”时，仅在“集合容器”之间计算最近中心（closestCenter），
     *    避免被集合内部的 Tab 容器干扰；
     * 2) 当拖拽的是“标签卡片（tab-*）”时，优先用指针位置命中（pointerWithin），若无命中再回退到矩形相交（rectIntersection），
     *    得到初步的 overId；若命中的是“集合容器”，并且该集合里有 Tab，则进一步在该集合下的“Tab 容器”之间用 closestCenter
     *    精确定位应当落到哪个 Tab 上/之间；
     * 3) 为了拖拽手感稳定，如果一瞬间没有命中任何可投放目标（例如高速移动或动画导致的空隙），
     *    则回退到上一次的命中结果 lastOverId；另外 recentlyMovedToNewContainer 用来处理跨集合移动后的短暂抖动。
     */
    const customCollisionDetection: CollisionDetection = useCallback(
        (args) => {
            // 当前正在被拖拽的 draggable 信息
            const {active} = args;
            const activeIdStr = active.id as string; // 统一转为字符串，便于我们用前缀判断类型

            // 情况 A：拖的是“集合卡片”。只在所有集合容器之间做最近中心判断，忽略所有 Tab 容器
            if (isCollectionId(activeIdStr)) {
                return closestCenter({
                    ...args,
                    // 只保留 id 以 collection- 开头的 droppable 容器（集合容器）
                    droppableContainers: args.droppableContainers.filter((container) =>
                        isCollectionId(container.id as string)
                    ),
                });
            }

            // 情况 B：拖的是“标签卡片”。优先采用基于鼠标指针位置的命中；如果没有命中，则回退到矩形区域相交命中
            const pointerIntersections = pointerWithin(args); // 指针所处位置覆盖到的所有 droppable
            const intersections =
                pointerIntersections.length > 0 ? pointerIntersections : rectIntersection(args);

            // 从命中集合里取第一个（由 dnd-kit 内部按权重排序），得到初步的 overId
            let overId = getFirstCollision(intersections, 'id');

            if (overId != null) {
                // 如果初步命中的是“集合容器”，我们希望进一步细化到该集合里的某个“Tab 容器”（更精确地插入）
                if (isCollectionId(overId as string)) {
                    const collectionId = parseCollectionId(overId as string);
                    if (collectionId != null) {
                        // 为了保证拖拽中的"所见即所得"，优先从预览快照里取集合；如果没有预览，则取当前真实展示数据
                        const preview = previewCollectionsRef.current;
                        const fromSnapshot = preview?.[collectionId];
                        const original = filteredCollections.find((item) => item.collectionId === collectionId);
                        const targetCollection = fromSnapshot ?? original; // 目标集合（用于判断是否有 Tab）

                        // 若该集合内存在 Tab，那么就将候选 droppable 限制为“Tab 容器”，再以最近中心做一次精确命中
                        if (targetCollection && targetCollection.tabs.length > 0) {
                            overId = closestCenter({
                                ...args,
                                // 过滤出“Tab 容器”：排除集合容器本身（overId）并且 id 前缀为 tab-
                                droppableContainers: args.droppableContainers.filter(
                                    (container) => container.id !== overId && isTabId(container.id as string)
                                ),
                            })[0]?.id; // 取最近的那个 Tab 容器的 id
                        }
                    }
                }

                // 记录这次命中的 id，用于后续在“短暂无命中”时作为兜底
                lastOverId.current = overId as string;
                // dnd-kit 期望返回形如 [{ id: string }] 的数组
                return [{id: overId as string}];
            }

            // 若本次没有命中任何容器，但刚刚发生过“跨集合移动”，
            // 则将 lastOverId 暂时设置为 activeId，避免瞬时抖动
            if (recentlyMovedToNewContainer.current) {
                lastOverId.current = activeId;
            }

            // 仍无有效命中，则回退到上一次的命中结果；否则返回空数组（表示没有命中）
            return lastOverId.current ? [{id: lastOverId.current}] : [];
        },
        // 依赖项：
        // - filteredCollections：用于读取当前/预览集合内是否存在 Tab
        // - isCollectionId / isTabId：前缀判断函数（稳定引用）
        [filteredCollections, isCollectionId, isTabId]
    );

    const handleDragEnd = useCallback(
        (event: DragEndEvent) => {
            const {active, over} = event;
            resetPreviewState();

            if (!over || active.id === over.id) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            const activeIdStr = active.id as string;
            const overIdStr = over.id as string;

            // 处理集合拖拽排序（应用Space方案）
            if (isCollectionId(activeIdStr)) {
                const activeCollectionId = parseCollectionId(activeIdStr);
                const overCollectionId = parseCollectionId(overIdStr);

                if (activeCollectionId == null || overCollectionId == null) {
                    setActiveId(null);
                    document.body.classList.remove('is-dragging');
                    return;
                }

                const fromIndex = filteredCollections.findIndex((c) => c.collectionId === activeCollectionId);
                const toIndex = filteredCollections.findIndex((c) => c.collectionId === overCollectionId);

                if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {
                    setActiveId(null);
                    document.body.classList.remove('is-dragging');
                    return;
                }

                if (!activeSpaceId) {
                    console.error('未选择活跃空间');
                    setActiveId(null);
                    document.body.classList.remove('is-dragging');
                    return;
                }

                // ===== 关键：在组件中同步更新缓存（避免异步延迟）=====
                // 保存原始缓存数据用于回滚
                const previousCollections = queryClient.getQueryData<CollectionWithTabsDto[]>(
                    QueryKeys.collections(activeSpaceId)
                );
                if (!previousCollections) {
                    setActiveId(null);
                    document.body.classList.remove('is-dragging');
                    return;
                }

                // 计算新排序
                const reordered = arrayMove(filteredCollections, fromIndex, toIndex);

                // 使用 calculateDragPositionUpdates 计算最小变更面
                const sortedIds = reordered.map(c => ({id: c.collectionId, position: c.position}));
                const updates = calculateDragPositionUpdates(
                    sortedIds.map((item, index) => ({
                        id: item.id,
                        position: reordered[index].position,
                    })),
                    toIndex
                );

                // 构建 API 请求数据
                const payload = {
                    items: updates.map(({id, position}) => ({
                        collection_id: id,
                        position,
                    })),
                };

                // 创建 position 映射
                const positionMap = new Map(updates.map(u => [u.id, u.position]));

                // 同步更新缓存
                queryClient.setQueryData<CollectionWithTabsDto[]>(
                    QueryKeys.collections(activeSpaceId),
                    (old) => {
                        if (!old) return old;

                        const updated = old.map(collection => {
                            const newPosition = positionMap.get(collection.collection.id);
                            if (newPosition !== undefined && newPosition !== collection.binding.position) {
                                return {
                                    ...collection,
                                    binding: {
                                        ...collection.binding,
                                        position: newPosition,
                                    },
                                };
                            }
                            return collection; // 保持原引用
                        });

                        return updated.sort((a, b) => a.binding.position - b.binding.position);
                    }
                );

                // 缓存已同步更新，清除 DragOverlay
                setActiveId(null);
                document.body.classList.remove('is-dragging');

                // 发送 API 请求（后台同步到服务器）
                reorderCollectionsMutation.mutate(
                    {spaceId: activeSpaceId, data: payload},
                    {
                        // 失败时使用组件保存的原始数据回滚
                        onError: () => {
                            queryClient.setQueryData<CollectionWithTabsDto[]>(
                                QueryKeys.collections(activeSpaceId),
                                previousCollections
                            );
                        },
                    }
                );

                return;
            }

            // 处理标签页拖拽（应用Space方案）
            const activeTabId = parseTabId(activeIdStr);
            const overTabId = parseTabId(overIdStr);
            const overCollectionCandidate = parseCollectionId(overIdStr);

            if (activeTabId == null) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            const activeCollectionId = resolveTabCollectionId(activeTabId);
            const overCollectionId =
                overCollectionCandidate ?? (overTabId != null ? resolveTabCollectionId(overTabId) : null);

            if (activeCollectionId == null || overCollectionId == null) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            const fromCollection = filteredCollections.find((item) => item.collectionId === activeCollectionId);
            const toCollection = filteredCollections.find((item) => item.collectionId === overCollectionId);
            if (!fromCollection || !toCollection || !activeSpaceId) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            const activeIndex = fromCollection.tabs.findIndex((tab) => tab.id === activeTabId);
            if (activeIndex === -1) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            // 保存原始缓存数据用于回滚
            const previousCollections = queryClient.getQueryData<CollectionWithTabsDto[]>(
                QueryKeys.collections(activeSpaceId)
            );
            if (!previousCollections) {
                setActiveId(null);
                document.body.classList.remove('is-dragging');
                return;
            }

            if (activeCollectionId === overCollectionId) {
                // 同集合内排序
                const overIndex = overTabId != null ? toCollection.tabs.findIndex((tab) => tab.id === overTabId) : -1;
                let targetIndex = overCollectionCandidate != null ? toCollection.tabs.length - 1 : overIndex;
                if (targetIndex < 0) {
                    targetIndex = toCollection.tabs.length - 1;
                }
                if (targetIndex < 0) {
                    setActiveId(null);
                    document.body.classList.remove('is-dragging');
                    return;
                }

                // 计算新的排序数据
                const reordered = arrayMove(toCollection.tabs, activeIndex, targetIndex);
                const payload = {
                    items: reordered.map((tab, index) => ({
                        item_id: tab.id,
                        from_collection_id: activeCollectionId,
                        to_collection_id: activeCollectionId,
                        position: index,
                    })),
                };

                // 同步更新缓存
                queryClient.setQueryData<CollectionWithTabsDto[]>(
                    QueryKeys.collections(activeSpaceId),
                    (old) => {
                        if (!old) return old;

                        return old.map(collection => {
                            if (collection.collection.id === activeCollectionId) {
                                // 将TabItem转为TabDto格式
                                const updatedItems = reordered.map((tab, index) => {
                                    // 从原items中找到对应的TabDto
                                    const originalDto = collection.items.find(t => t.id === tab.id);
                                    if (originalDto) {
                                        return {
                                            ...originalDto,
                                            position: index,
                                        };
                                    }
                                    // 如果找不到，创建新的（不应该发生）
                                    return {
                                        id: tab.id,
                                        collection_id: activeCollectionId,
                                        url: tab.url,
                                        title: tab.title,
                                        domain: tab.domain,
                                        favicon: tab.favicon,
                                        position: index,
                                        is_pin: tab.isPinned,
                                        owner_id: 0,
                                        created_at: new Date().toISOString(),
                                        updated_at: new Date().toISOString(),
                                    };
                                });
                                return {
                                    ...collection,
                                    items: updatedItems,
                                };
                            }
                            return collection;
                        });
                    }
                );

                // 清除 DragOverlay
                setActiveId(null);
                document.body.classList.remove('is-dragging');

                // 发送 API 请求
                reorderTabsMutation.mutate(payload, {
                    onError: () => {
                        queryClient.setQueryData<CollectionWithTabsDto[]>(
                            QueryKeys.collections(activeSpaceId),
                            previousCollections
                        );
                    },
                });
            } else {
                // 跨集合移动
                let targetIndex = toCollection.tabs.length;
                if (overTabId != null) {
                    const overIndex = toCollection.tabs.findIndex((tab) => tab.id === overTabId);
                    const isBelowOverItem =
                        over &&
                        active.rect.current.translated &&
                        active.rect.current.translated.top > over.rect.top + over.rect.height;
                    const modifier = isBelowOverItem ? 1 : 0;
                    targetIndex = overIndex >= 0 ? overIndex + modifier : toCollection.tabs.length;
                }

                const clampedTargetIndex = Math.max(0, Math.min(targetIndex, toCollection.tabs.length));

                // 使用 reorderTabs API 进行跨集合移动
                const payload = {
                    items: [
                        {
                            item_id: activeTabId,
                            from_collection_id: activeCollectionId,
                            to_collection_id: overCollectionId,
                            position: clampedTargetIndex,
                        },
                    ],
                };

                // 同步更新缓存
                queryClient.setQueryData<CollectionWithTabsDto[]>(
                    QueryKeys.collections(activeSpaceId),
                    (old) => {
                        if (!old) return old;

                        const activeTab = fromCollection.tabs.find(t => t.id === activeTabId);
                        if (!activeTab) return old;

                        return old.map(collection => {
                            // 从源集合移除
                            if (collection.collection.id === activeCollectionId) {
                                return {
                                    ...collection,
                                    items: collection.items
                                        .filter(t => t.id !== activeTabId)
                                        .map((tab, index) => ({...tab, position: index})),
                                };
                            }
                            // 添加到目标集合
                            if (collection.collection.id === overCollectionId) {
                                // 从源集合的items中找到对应的TabDto
                                const sourceCollection = old.find(c => c.collection.id === activeCollectionId);
                                const sourceTabDto = sourceCollection?.items.find(t => t.id === activeTabId);

                                if (!sourceTabDto) return collection;

                                const newItems = [...collection.items];
                                newItems.splice(clampedTargetIndex, 0, {
                                    ...sourceTabDto,
                                    collection_id: overCollectionId,
                                });
                                return {
                                    ...collection,
                                    items: newItems.map((tab, index) => ({...tab, position: index})),
                                };
                            }
                            return collection;
                        });
                    }
                );

                // 清除 DragOverlay
                setActiveId(null);
                document.body.classList.remove('is-dragging');

                // 发送 API 请求
                reorderTabsMutation.mutate(payload, {
                    onError: () => {
                        queryClient.setQueryData<CollectionWithTabsDto[]>(
                            QueryKeys.collections(activeSpaceId),
                            previousCollections
                        );
                    },
                });
            }
        },
        [
            activeSpaceId,
            filteredCollections,
            queryClient,
            reorderCollectionsMutation,
            reorderTabsMutation,
            resolveTabCollectionId,
            resetPreviewState,
        ]
    );

    const handleDragStart = useCallback(
        (event: DragStartEvent) => {
            const {active} = event;
            const activeIdStr = active.id as string;
            setActiveId(activeIdStr);
            document.body.classList.add('is-dragging');

            if (isTabId(activeIdStr)) {
                ensurePreviewState();
            }
        },
        [ensurePreviewState]
    );

    const handleDragCancel = useCallback(
        (_event: DragCancelEvent) => {
            resetPreviewState();
            setActiveId(null);
            document.body.classList.remove('is-dragging');
        },
        [resetPreviewState]
    );

    const handleDragOver = useCallback(
        (event: DragOverEvent) => {
            const {active, over} = event;
            if (!over || !active) return;

            const activeIdStr = active.id as string;
            if (isCollectionId(activeIdStr)) return;

            const activeTabId = parseTabId(activeIdStr);
            if (activeTabId == null) return;

            const previewMap = ensurePreviewState();
            const activeCollectionId = resolveTabCollectionId(activeTabId);
            if (activeCollectionId == null) return;

            const overIdStr = over.id as string;
            const overCollectionCandidate = parseCollectionId(overIdStr);
            const overTabId = parseTabId(overIdStr);
            const targetCollectionId = overCollectionCandidate ?? (overTabId != null ? resolveTabCollectionId(overTabId) : null);
            if (targetCollectionId == null) return;

            const sourceCollection = previewMap[activeCollectionId];
            const targetCollection = previewMap[targetCollectionId];
            if (!sourceCollection || !targetCollection) return;

            const activeIndex = sourceCollection.tabs.findIndex((tab) => tab.id === activeTabId);
            if (activeIndex === -1) return;
            const activeTab = sourceCollection.tabs[activeIndex];
            const isSameContainer = activeCollectionId === targetCollectionId;

            const targetTabsWithoutActive = targetCollection.tabs.filter((tab) => tab.id !== activeTabId);
            const overTabIdForPreview = parseTabId(overIdStr);
            let targetIndex: number;
            if (overCollectionCandidate != null) {
                targetIndex = targetTabsWithoutActive.length;
            } else {
                const overIndex = overTabIdForPreview != null ? targetCollection.tabs.findIndex((tab) => tab.id === overTabIdForPreview) : -1;
                const isBelowOverItem =
                    over &&
                    active.rect.current.translated &&
                    active.rect.current.translated.top > over.rect.top + over.rect.height;
                const modifier = isBelowOverItem ? 1 : 0;
                targetIndex = overIndex >= 0 ? overIndex + modifier : targetCollection.tabs.length;
            }

            const clampedIndex = Math.max(0, Math.min(targetIndex, targetTabsWithoutActive.length));

            if (isSameContainer) {
                const reorderedTabs = arrayMove(targetCollection.tabs, activeIndex, clampedIndex).map((tab: TabItem, index) => ({
                    ...tab,
                    position: index,
                }));
                previewMap[targetCollectionId] = {
                    ...targetCollection,
                    tabs: reorderedTabs,
                };
            } else {
                const nextSourceTabs = sourceCollection.tabs
                    .filter((tab) => tab.id !== activeTabId)
                    .map((tab: TabItem, index) => ({...tab, position: index}));

                const insertedTabs = [
                    ...targetTabsWithoutActive.slice(0, clampedIndex),
                    {...activeTab, collectionId: targetCollectionId},
                    ...targetTabsWithoutActive.slice(clampedIndex),
                ].map((tab: TabItem, index) => ({...tab, position: index}));

                previewMap[activeCollectionId] = {
                    ...sourceCollection,
                    tabs: nextSourceTabs,
                };

                previewMap[targetCollectionId] = {
                    ...targetCollection,
                    tabs: insertedTabs,
                };

                const previewLocations = previewTabLocationsRef.current ?? new Map<number, number>();
                previewLocations.set(activeTabId, targetCollectionId);
                previewTabLocationsRef.current = previewLocations;
                recentlyMovedToNewContainer.current = true;
            }

            previewCollectionsRef.current = previewMap;
            setPreviewCollections({...previewMap});
        },
        [ensurePreviewState, resolveTabCollectionId]
    );

    /**
     * renderedCollections：
     * - 作用：决定"当前渲染用的集合列表"。
     * - 思路：如果存在拖拽中的预览快照（previewCollections），优先用快照中的集合（带预排序/跨集合临时移动的视觉效果）；
     *         否则回退到真实的 filteredCollections。
     * - 好处：实现"所见即所得"的拖拽预览，不影响真实数据源。
     */
    const renderedCollections = React.useMemo(() => {
        // 没有预览态：直接渲染基础集合
        if (!previewCollections) {
            return filteredCollections;
        }
        // 有预览态：逐个集合按 collectionId 用快照中的集合替换，若该集合未被预览修改则用原对象
        return filteredCollections.map((collection) =>
            previewCollections[collection.collectionId] ?? collection
        );
        // 依赖：真实集合变化或预览快照变化时重新计算渲染用数组
    }, [filteredCollections, previewCollections]);

    /**
     * 将 recentlyMovedToNewContainer 标记在下一帧复位：
     * - 背景：拖拽 Tab 跨集合时，为了稳定碰撞检测会临时把 recentlyMovedToNewContainer 置为 true，
     *         用来在"短暂无命中"时回退到上一次命中，避免抖动。
     * - 这里在浏览器下一帧（requestAnimationFrame）把该标记复位为 false，时间刚好跨过一次渲染，
     *   既能稳定当前帧的判定，又不会让这个"跨容器放松期"持续太久。
     */
    React.useEffect(() => {
        if (recentlyMovedToNewContainer.current) {
            // 在下一帧复位标记
            const id = requestAnimationFrame(() => {
                recentlyMovedToNewContainer.current = false;
            });
            // 组件或依赖更新时确保取消该帧回调，避免潜在的泄漏或误调
            return () => cancelAnimationFrame(id);
        }
        // 若标记为 false，不做任何事
        return undefined;
        // 依赖：当渲染用数据源或预览快照变化时，重新评估是否需要复位该标记
    }, [filteredCollections, previewCollections]);

    /**
     * dropAnimation：拖拽释放动画效果的配置
     * 应用Space的方案：配置快速释放动画
     */
    const dropAnimation: DropAnimation = {
        duration: 150,  // 释放动画时长（默认 250ms）
        easing: 'cubic-bezier(0.18, 0.67, 0.6, 1.22)',  // 缓动函数
        sideEffects: defaultDropAnimationSideEffects({
            styles: {
                active: {
                    opacity: '0.5', // 拖拽中的浮层半透明
                },
            },
        }),
    };

    return (
        <ScrollContainer className="w-full h-full px-6" padRightForScrollbar={false}>
            {isLoading && filteredCollections.length === 0 && (
                <div
                    className="flex flex-col items-center justify-center py-32 text-center text-gray-500 dark:text-gray-400">
                    <Loader className="mb-4 h-6 w-6 animate-spin"/>
                    <p className="text-sm">集合加载中，请稍候…</p>
                </div>
            )}

            {/* 搜索无结果（优先显示） */}
            {!isLoading && searchQuery && filteredCollections.length === 0 && (
                <div
                    className="flex flex-col items-center justify-center py-32 text-center text-gray-500 dark:text-gray-400">
                    <Search size={32} className="mb-2"/>
                    <h2 className="text-lg font-semibold mb-1">没有找到匹配的集合</h2>
                    <p className="text-sm">尝试使用不同的关键词搜索</p>
                </div>
            )}

            {/* 空间完全没有集合（无搜索时才显示） */}
            {!isLoading && !searchQuery && filteredCollections.length === 0 && (
                <div
                    className="flex flex-col items-center justify-center py-32 text-center text-gray-500 dark:text-gray-400">
                    <FolderOpen size={48} className="mb-4 opacity-50"/>
                    <h2 className="text-lg font-semibold mb-1">还没有任何集合</h2>
                    <p className="text-sm mb-4">点击下方按钮保存当前窗口的标签页</p>
                    <Button onClick={onAddCollection} disabled={isSaving}>
                        {isSaving ? '保存中…' : '保存当前窗口'}
                    </Button>
                </div>
            )}

            {filteredCollections.length > 0 && (
                isDragEnabled ? (
                    <DndContext
                        collisionDetection={customCollisionDetection}
                        measuring={{
                            droppable: {
                                strategy: MeasuringStrategy.Always,
                            },
                        }}
                        onDragStart={handleDragStart}
                        onDragOver={handleDragOver}
                        onDragEnd={handleDragEnd}
                        onDragCancel={handleDragCancel}
                    >
                        <SortableContext
                            items={renderedCollections.map((collection) => `${collectionPrefix}${collection.collectionId}`)}
                            strategy={verticalListSortingStrategy}
                        >
                            <div className="flex flex-col">
                                {renderedCollections.map((collection) => (
                                    <DraggableCollectionCard
                                        key={collection.collectionId}
                                        id={`${collectionPrefix}${collection.collectionId}`}
                                        collection={collection}
                                        defaultExpanded
                                        disableDrag={!isDragEnabled}
                                        isSortingContainer={isCollectionId(activeId ?? '')}
                                    />
                                ))}
                            </div>
                        </SortableContext>

                        <DragOverlay dropAnimation={dropAnimation}>
                            {activeId ? (
                                (() => {
                                    if (isCollectionId(activeId)) {
                                        const collectionId = parseCollectionId(activeId);
                                        if (collectionId == null) return null;
                                        const current = renderedCollections.find((item) => item.collectionId === collectionId);
                                        if (!current) return null;
                                        return (
                                            <CollectionCard collection={current} className="h-full" defaultExpanded/>
                                        );
                                    }

                                    const tabId = parseTabId(activeId);
                                    if (tabId == null) return null;

                                    // 直接从 renderedCollections 中查找包含该 tab 的集合
                                    const collection = renderedCollections.find((coll) =>
                                        coll.tabs.some((tab) => tab.id === tabId)
                                    );
                                    if (!collection) return null;

                                    const currentTab = collection.tabs.find((tab) => tab.id === tabId);
                                    if (!currentTab) return null;

                                    return (
                                        <TabCard
                                            key={currentTab.id}
                                            id={activeId}
                                            tab={currentTab}
                                            collectionId={collection.collectionId}
                                            disableSorting
                                            isAnyDragging={false}
                                            isDragOverlay
                                        />
                                    );
                                })()
                            ) : null}
                        </DragOverlay>
                    </DndContext>
                ) : (
                    <div className="flex flex-col">
                        {renderedCollections.map((collection) => (
                            <CollectionCard
                                key={collection.collectionId}
                                collection={collection}
                                defaultExpanded
                            />
                        ))}
                    </div>
                )
            )}

            {!isLoading && filteredCollections.length > 0 && (
                <div className="text-center pt-8 mt-6 mb-12 text-sm text-gray-500 dark:text-gray-400">
                    <span>{collectionsCount} 个集合 · {totalTabsCount} 个标签</span>
                </div>
            )}
        </ScrollContainer>
    );
};
